// stacktrace.h (c) 2008, Timo Bingmann from http://idlebox.net/
// published under the WTFPL v2.0

#ifndef _STACKTRACE_H_
#define _STACKTRACE_H_

#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <cxxabi.h>

namespace SAMRAI {
namespace tbox {

/** Print a demangled stack backtrace of the caller function to FILE* out. */
static inline void print_stacktrace(FILE* out = stderr, unsigned int max_frames = 63, int expectedRank=0)
{
   SAMRAI_MPI samrai_mpi(SAMRAI_MPI::getSAMRAIWorld());
   int actualRank = samrai_mpi.getRank();
   
   if(actualRank == expectedRank) {
      // storage array for stack trace address data
      void* addrlist[max_frames + 1];

      // retrieve current stack addresses
      int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));

      if (addrlen == 0) {
         fprintf(out, "  <empty, possibly corrupt>\n");
         return;
      }

      fprintf(out,"+++STACK\n");

      // resolve addresses into strings containing "filename(function+address)",
      // this array must be free()-ed
      char** symbollist = backtrace_symbols(addrlist, addrlen);

      // allocate string which will be filled with the demangled function name
      size_t funcnamesize = 4096;
      char* funcname = (char*)malloc(funcnamesize);

      // iterate over the returned symbol lines. skip the first, it is the
      // address of this function.
      for (int i = 1; i < addrlen; i++) {
         char *begin_name = 0, *begin_offset = 0, *end_offset = 0;

         // find parentheses and +address offset surrounding the mangled name:
         // ./module(function+0x15c) [0x8048a6d]
         for (char* p = symbollist[i]; *p; ++p) {
            if (*p == '(')
               begin_name = p;
            else if (*p == '+')
               begin_offset = p;
            else if (*p == ')' && begin_offset) {
               end_offset = p;
               break;
            }
         }

         if (begin_name && begin_offset && end_offset && begin_name < begin_offset) {
            *begin_name++ = '\0';
            *begin_offset++ = '\0';
            *end_offset++ = '\0';

            // mangled name is now in [begin_name, begin_offset) and caller
            // offset in [begin_offset, end_offset). now apply
            // __cxa_demangle():

            int status;
            char* ret = abi::__cxa_demangle(begin_name,
                                            funcname, &funcnamesize, &status);
            if (status == 0) {
               funcname = ret;  // use possibly realloc()-ed string
               fprintf(out, "  %s : %s+%s %s\n",
                       symbollist[i], funcname, begin_offset, end_offset);
            } else {
               // demangling failed. Output function name as a C function with
               // no arguments.
               fprintf(out, "  %s : %s()+%s %s\n",
                       symbollist[i], begin_name, begin_offset, end_offset);
            }
         } else {
            // couldn't parse the line? print the whole line.
            fprintf(out, "  %s\n", symbollist[i]);
         }
      }

      fprintf(out,"---STACK\n");
      free(funcname);
      free(symbollist);
   } // if test for rank
}

} // namespace tbox
} // namespace SAMRAI

#endif  // _STACKTRACE_H_
